module RBPM
  module Engine
    include RBPM::Parser
    include RBPM::Toolkit
    include RBPM::Params
    attr_reader :process_instance
    attr_reader :doc
    attr_reader :view_config
    attr_reader :node_name

    def initialize(node, doc, process_instance, params={})
      @process_instance = process_instance
      @doc = doc.instance_of?(String) ? read(doc) : doc
      # 将所有节点的属性变成本实例属性
      node_attributes_to_attr node
      # 查找本节点的视图配置
      view_config_generate
      # 上一个节点
      @last = params[:last]
      # 业务参数
      @parameters = params[:parameters]
      self
    end

    # 找到某文档开始事件，并实例化
    def self.start_event(doc, process_instance)
      start_event=RBPM::Parser.node(doc, 'startEvent')
      RBPM::Event.new(start_event, doc, process_instance)
    end

    # 引擎完成
    def engine_finish!
      process_instance.finish!
    end

    protected

    # 在数据库中查找，当前活动是否已经存在
    def working_active_exist?
      @process_instance.working_active_instances.where(bpmn_active_define_id: id).exists?
    end

    # 在数据库中查找已经存在的活动实例
    def active_instance
      @process_instance.process_active_instances.find_by bpmn_active_define_id: id
    end

    # 创建活动实例
    def create_active_instance
      # 在数据库中查找，当前活动是否已经存在
      if working_active_exist?
        @active = active_instance
      else
        # 从数据库中读取进程变量数据
        process_variables_init @process_instance
        eval(beforeStart) if defined? beforeStart
        @active = create_active
        eval(beforeStart) if defined? afterStart
      end
      @active
    end

    # 生成下一个顺序流
    def next_flow
      nodes= nodes("sequenceFlow[sourceRef=#{id}]")
      # if self.class != RBPM::ParallelGateway && self.class != RBPM::ExclusiveGateway && nodes.length!=1
      #   raise RBPM::DesignError.new("multiple Flow after #{self.class.name}")
      # end
      if nodes.length>0
        sequence_flow nodes.first
      else
        nil
      end
    end

    private

    # 通过查找viewConfig，来生成字典
    def view_config_generate
      @view_config = {}

      # 针对流程的视图配置
      if node('process')[:viewConfig]
        process_view_config = node("##{node('process')[:viewConfig]}")
        process_view_config.children.css('*').each do |node|
          @view_config[node.node_name.to_sym] = node.content
        end
      end

      if @viewConfig
        # 针对节点的视图配置
        view_config = node("##{@viewConfig}")
        view_config.children.css('*').each do |node|
          @view_config[node.node_name.to_sym] = node.content
        end
      end
      @view_config
    end

    # 定义本节点所有标签记录的值
    def node_attributes_to_attr(node)
      @node_name = node.node_name
      node.each do |k, v|
        # 定义可读属性
        define_singleton_method k.to_sym do
          instance_variable_set("@#{k}", script_detect(k, v))
        end
      end
    end

    # 尝试发现脚本，若标签值是脚本，则返回脚本代码
    def script_detect(k, v)
      return v if %w(id name sourceRef targetRef viewConfig).index k
      the_node = node("##{v}")
      the_node.present? ? the_node.content.strip : v
    end
  end
end